﻿// ===================================
// <copyright>MethodWorx Ltd</copyright>
// <author>James Simpson</author>
// <email>mailto:james@methodworx.com</email>
// <created>07 June 2011</created>
// <lastedit>07 June 2011</lastedit>
//This file is distributed under the Microsoft Public License (Ms-PL)
//See License.txt for details
// ===================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MethodWorx.CMS.Plugins;
using MethodWorx.CMS.TemplateParser;
using MethodWorx.CMS.UI.Areas.Application.Models;
using MethodWorx.CMS.UI.Application;
using MethodWorx.Core.DomainModel;
using MethodWorx.CMS.TemplateLanguage;
using MethodWorx.Core.Extensions;
using MethodWorx.CMS.TemplateLanguage.Properties;
using System.Xml;
using MethodWorx.CMS.Services;
using MethodWorx.CMS.UI.Application.ActionResults;
using MethodWorx.CMS.Configuration;

namespace MethodWorx.CMS.UI.Areas.Application.Controllers
{
    public class TemplateController : ApplicationController
    {
        private IModelFactory<TemplateModel> modelFactory;
        private SchemaService service;

        public TemplateController(IModelFactory<TemplateModel> modelFactory, SchemaService service)
        {
            this.modelFactory = modelFactory;
            this.service = service;
        }
        //
        // GET: /Application/Template/
        [HttpGet]
        public ActionResult Editor(Guid id)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            model.Parsers = DefaultParserFactory.GetParsers().ToArray();
            model.ParserNamespaces = DefaultParserFactory.GetParsers().Select(p => p.Namespace.ToLower().FirstCharToUpper()).Distinct().ToArray();

            return View(model);            
        }



        [HttpGet]
        public ActionResult Preview(Guid id, bool? __css, bool? __script)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);


            if((__css ?? false) == true)
            {
                return Content(model.PageTemplate.GetCss());
            }
            else if((__script ?? false) == true)
            {
                return Content(model.PageTemplate.GetScript());
            }
            else
            {
                this.ViewData["Id"] = model.PageTemplate.Id;
                this.ViewData["Template"] = model.GetParsedTemplate(Url.Content("~/"));
                return View();
            }
        }

        public ActionResult InsertParser(Guid id, int index, string parser, string position)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            var engine = model.GetTemplateParser();
            var foundParser = engine.GetParser(index);
            if (foundParser != null)
            {
                //
                //  ok, we want to position this parser near the found one
                var changed = engine.InsertParser(foundParser, parser, position);
                if (changed != null)
                {
                    //foreach(Page template engine.Templates
                    //service.SaveTemplate(changed.Id, changed.Template);
                    model.TemporarySaveTemplate(changed.Id, changed.Template);
                }
            }
            return Content("");
        }

        public JsonResult DeleteParser(Guid id, int index)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            var engine = model.GetTemplateParser();
            var foundParser = engine.GetParser(index);
            if (foundParser != null)
            {
                //
                //  ok, we want to position this parser near the found one
                var changed = engine.DeleteParser(foundParser);
                if (changed != null)
                {
                    //foreach(Page template engine.Templates
                    model.TemporarySaveTemplate(changed.Id, changed.Template);
                    return Json(new { Deleted = true }, JsonRequestBehavior.AllowGet);
                }
            }
            return Json(new { Deleted = false }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult DeleteElement(Guid id, string pi)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            var engine = model.GetTemplateParser();
            TemplateElement element = null;
            if (pi != null)
            {
                var p = int.Parse(pi.Split(':')[0]);
                var i = int.Parse(pi.Split(':')[1]);
                element = engine.FindDesignModelElementById(p, i);
            }
            if (element != null)
            {
                //
                //  ok, we want to position this parser near the found one
                var changed = engine.DeleteElement(element);
                if (changed != null)
                {
                    //foreach(Page template engine.Templates
                    model.TemporarySaveTemplate(changed.Id, changed.Template);
                    return Json(new { Deleted = true }, JsonRequestBehavior.AllowGet);
                }
            }
            return Json(new { Deleted = false }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult SetParserProperty(Guid id, int index, string name, string value)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            var engine = model.GetTemplateParser();
            var parser = engine.GetParser(index);
            if (parser != null)
            {
                //  ok, we have the parser
                var changed = engine.SetParserProperty(parser, name, value);
                if(changed != null)
                {
                    //foreach(Page template engine.Templates
                    model.TemporarySaveTemplate(changed.Id, changed.Template);

                    //
                    //  parse the template
                    model = this.modelFactory.CreateModel(this.CurrentUser);
                    model.Initialize(id);
                    engine = model.GetTemplateParser();
                    
                    return Json(new {Changed = true, Content = engine.GetTemplateForDesignMode()}, JsonRequestBehavior.AllowGet);

                }
                //
                
            }
            return Json(new {Changed = false } , JsonRequestBehavior.AllowGet);
        }

        [HttpGet]
        public ActionResult Properties(Guid id, int index)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            //
            //  ok, we have the parser, so lets get the index
            List<object> properties = new List<object>();

            
            var template = model.GetTemplateParser();
            var parser = template.GetParser(index);

            var element = template.GetParsedElement(index);

            foreach (var property in parser.GetType().GetProperties())
            {
                foreach (var custom in property.GetCustomAttributes(true))
                {
                    if (custom is PropertyAttribute)
                    {
                        var pa = custom as PropertyAttribute;
                        if (string.IsNullOrEmpty(pa.Alias))
                            pa.Alias = property.Name;

                        properties.Add(new
                        {
                            Name = pa.Alias ?? "",
                            AttributeType = pa.GetType().Name ?? "",
                            Value = property.GetValue(parser, null) ?? "",
                            PropertyType = property.PropertyType.Name ?? ""
                        });
                    }
                }
            }

            return Json(properties.ToArray(), JsonRequestBehavior.AllowGet);
        }


        [HttpPost]
        [ValidateInput(false)]
        public JsonResult Save(Guid id, string code)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            model.Complete(code);

            return Json(new { success = true });
        }


        [HttpPost]
        [ValidateInput(false)]
        public JsonResult SaveTemp(Guid id, string code)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            model.TemporarySaveTemplate(id, code);
            try
            {
                var errors = model.GetTemplateParser().Parser.Document.GetErrors();
                if (errors.Count() > 0)
                {
                    var errorText = "The template contains errors, please correct before continuing:\r\n\r\n";
                    foreach (var error in errors)
                    {
                        errorText += error + "\r\n";
                    }
                    return Json(new { success = false, message = errorText });
                }
                else
                    return Json(new { success = true });
            }
            catch (Exception ex)
            {
                return Json(new { success = false, message = ex.Message});
            }
            
        }

        [ValidateInput(false)]
        public JsonResult SaveFromDesigner(Guid id)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            model.SaveFromDesigner();

            return Json(new { success = true }, JsonRequestBehavior.AllowGet);
        }
        public JsonResult Get(Guid id)
        {
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            return Json(new { success = true, Code = model.Code }, JsonRequestBehavior.AllowGet);
        }

        [ValidateInput(false)]
        public JsonResult ReplaceParser(Guid id, string pi, string index, string parser, string initializer)
        {
            //  get the index and the parser id


            //  now find the nodes
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);
            TemplateElement element = null;


            var engine = model.GetTemplateParser();
            if (pi != null)
            {
                var p = int.Parse(pi.Split(':')[0]);
                var i = int.Parse(pi.Split(':')[1]);
                element = engine.FindDesignModelElementById(p, i);
            }
            else if(index != null)
            {
                element = engine.GetParserElement(int.Parse(index));
            }

            if(element != null)
            {
                var changed = engine.ReplaceElementWithParser(element, parser, initializer);
                //foreach(Page template engine.Templates
                model.TemporarySaveTemplate(changed.Id, changed.Template);

                //
                //  parse the template
                model = this.modelFactory.CreateModel(this.CurrentUser);
                model.Initialize(id);
                engine = model.GetTemplateParser();

                return Json(new { Changed = true, Content = engine.GetTemplateForDesignMode() }, JsonRequestBehavior.AllowGet);
            }

            return Json(new { Changed = false }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult CanMakePageProperty(Guid id, string pi)
        {
            //
            //  get the index and the parser id
            var p = int.Parse(pi.Split(':')[0]);
            var i = int.Parse(pi.Split(':')[1]);
            
            //  now find the nodes
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            var element = model.GetTemplateParser().FindDesignModelElementById(p, i);

            //
            //  ok, does the element have any children that are potentially parsers
            if (element.FlattenNodes<TemplateElement>().Where(te => te.Name.Contains(":")).Any())
            {
                return
                    Json(
                        new
                            {
                                Success = true,
                                CanMakePageProperty = false,
                                Message =
                            "Can not make this element a page property becuase it contains controls / template logic"
                            }, JsonRequestBehavior.AllowGet);
            }
            else
            {
                return Json(new { Success = true, CanMakePageProperty = true }, JsonRequestBehavior.AllowGet);
            }
        }

        [HttpGet]
        public ViewResult MakePageProperty(Guid id, string pi)
        {
            //
            //  get the index and the parser id
            var p = int.Parse(pi.Split(':')[0]);
            var i = int.Parse(pi.Split(':')[1]);

            //  now find the nodes
            var model = this.modelFactory.CreateModel(this.CurrentUser);
            model.Initialize(id);

            var element = model.GetTemplateParser().FindDesignModelElementById(p, i);

            //
            //  ok, does the element have any children that are potentially parsers
            if (element.FlattenNodes<TemplateElement>().Where(te => te.Name.Contains(":")).Any())
            {
                this.ViewData["error"] = "Can not make this element a page property becuase it contains controls / template logic";
                return View("SimpleError");
            }
            else
            {
                //
                //  else, here we go, time to make a pag eproperty
                return View(new MakePagePropertyModel(element, this.service, model.PageTemplate));
            }
        }

        public JsonResult GetParserInfo(string parser)
        {
            var found =
                DefaultParserFactory.GetParsers().FirstOrDefault(
                    p => p.Namespace.ToLower() + ":" + p.Name.ToLower() == parser.ToLower().Trim());

            if (found != null && found is IMetaParser)
            {
                return Json(new { success = true, Description = ((IMetaParser)found).GetDescription() }, JsonRequestBehavior.AllowGet);
            }
            else
            {
                return Json(new { success = false }, JsonRequestBehavior.AllowGet);
            }
        }

        public JsonResult GetParserParameters(string parser, string parameter)
        {
            var found =
                DefaultParserFactory.GetParsers().FirstOrDefault(
                    p => p.Namespace.ToLower() + ":" + p.Name.ToLower() == parser.ToLower().Trim());

            if (found != null && found is IMetaParser)
            {
                //
                //  ok, lets get the parameters
                var metaParser = found as IMetaParser;
                var parameters = GetParametersFromProperties(metaParser, parameter).ToArray();
                parameters = metaParser.GetParameters(null, parameters);

                foreach (var p in parameters)
                {
                    if (p.Name.ToLower().StartsWith(parameter))
                        return
                            Json(
                                new
                                    {
                                        parserDescription = metaParser.GetDescription(),
                                        success = true,
                                        description = p.Description,
                                        found = parameters.ToArray(),
                                    }, JsonRequestBehavior.AllowGet);
                }

                return Json(new { parserDescription = metaParser.GetDescription(), success = true, found = parameters.ToArray() }, JsonRequestBehavior.AllowGet);
            }
            
            return Json(new { success = false }, JsonRequestBehavior.AllowGet);
        }

        private IEnumerable<ParserMetaParameter> GetParametersFromProperties(IMetaParser found, string parameterName)
        {
            var attrs = ParserPropertyHelper.GetPropertyAttributes(found.GetType());
            foreach (var attr in attrs)
            {
                yield return new ParserMetaParameter()
                                 {
                                     Description = attr.Description,
                                     Name = attr.Alias ?? attr.OriginalName,
                                     Options = attr.Options
                                 };
            }
        }


        [HttpPost]
        public ActionResult MakePageProperty(Guid id, string pi, string submit, FormCollection form)
        {
            if (submit == "cancel")
                return new CloseDialogResult();
            else
            {
                //
                //  get the index and the parser id
                var p = int.Parse(pi.Split(':')[0]);
                var i = int.Parse(pi.Split(':')[1]);

                //  now find the nodes
                var model = this.modelFactory.CreateModel(this.CurrentUser);
                model.Initialize(id);

                var element = model.GetTemplateParser().FindDesignModelElementById(p, i);

                //
                //  ok, does the element have any children that are potentially parsers
                if (element.FlattenNodes<TemplateElement>().Where(te => te.Name.Contains(":")).Any())
                {
                    this.ViewData["error"] =
                        "Can not make this element a page property becuase it contains controls / template logic";
                    return View("SimpleError");
                }
                else
                {
                    var makePropertyModel = new MakePagePropertyModel(element, this.service, model.PageTemplate);
                    this.TryUpdateModel(makePropertyModel, form);

                    if (this.ModelState.IsValid)
                    {
                        //
                        //  ok, firstly lets create the property
                        var property = new PropertyConfiguration().GetPropertyConfigurations().FirstOrDefault(pr => pr.Name == makePropertyModel.PropertyType);

                        if (property == null)
                            this.ViewData["error"] = "Invalid Property Type";
                        else
                        {
                            //
                            //  ok, we have our property, time to create one
                            var details = (ICustomPropertyDetails)Activator.CreateInstance(property.GetDetailsType());
                            //  ok, parse the details
                            //details.Parse(makePropertyModel.PropertyValue);
                            //  now its time to create
                            service.AddPageTemplateProperty(makePropertyModel.Template.Id,
                                                            makePropertyModel.PropertyName,
                                                            makePropertyModel.PropertyType,
                                                            details);

                            return new CloseDialogResult();
                        }

                    }
                    
                    //
                    //  else, here we go, time to make a pag eproperty
                    return View(makePropertyModel);
                }
            }
        }
    }
}
